package com.xian.wsim.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.xian.wsim.entity.MessageInfo;
import com.xian.wsim.exception.WsimException;
import com.xian.wsim.service.MessageService;
import com.xian.wsim.service.UserOnlineService;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.net.URI;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CompletableFuture;

@Component
@Slf4j
public class MyWebSocketHandler extends TextWebSocketHandler {

    private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
    private static final Map<String, LocalDateTime> lastActiveTimeMap = new ConcurrentHashMap<>();

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private UserOnlineService userOnlineService;

    @Autowired
    private Gson gson;

    @Autowired
    private MessageService messageService;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String userId = extractUserIdFromSession(session);
        if (!StringUtil.isNullOrEmpty(userId)) {
            sessions.put(userId, session);
            lastActiveTimeMap.put(userId, LocalDateTime.now());
            userOnlineService.setUserOnline(Long.valueOf(userId));
        } else {
            log.error("用户ID为空，非法请求。。。");
            session.close(CloseStatus.GOING_AWAY.withReason("Inactive User"));
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String userId = extractUserIdFromSession(session);
        try{
            if(StringUtil.isNullOrEmpty(userId)) {
                throw new WsimException("用户ID为空，非法请求。。。");
            }
            lastActiveTimeMap.put(userId, LocalDateTime.now());
            MessageInfo msg = gson.fromJson(message.getPayload(), MessageInfo.class);
            String toUserId = String.valueOf(msg.getReceiverId());
            WebSocketSession toSession = sessions.get(toUserId);
            if (toSession != null && toSession.isOpen()) {
                toSession.sendMessage(new TextMessage(gson.toJson(msg)));
            }
            String sessionId = getSessionId(msg.getSenderId(), msg.getReceiverId());
            saveMessageToRedis(sessionId, msg);

            // 异步保存消息到数据库
            msg.setSessionId(sessionId);
            CompletableFuture<Void> saveToDBFuture = messageService.saveMessageToDBAsync(msg);
            saveToDBFuture.exceptionally(ex -> {
                log.error("异步保存消息到数据库失败: {}", ex.getMessage());
                return null;
            });
        } catch (Exception e) {
            log.error("消息处理异常，关闭连接，exception:{0},{1}", e,userId);
            if(!StringUtil.isNullOrEmpty(userId)) {
                sessions.remove(userId);
            }
        }
    }

    private String extractUserIdFromSession(WebSocketSession session) {
        URI uri = session.getUri();
        if (uri != null) {
            String query = uri.getQuery();
            if (query != null) {
                Map<String, String> queryParams = UriComponentsBuilder.fromUri(uri).build().getQueryParams().toSingleValueMap();
                return queryParams.get("userId");
            }
        }
        return null;
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String userId = extractUserIdFromSession(session);
        if (!StringUtil.isNullOrEmpty(userId)) {
            sessions.remove(userId);
            lastActiveTimeMap.remove(userId);
            userOnlineService.setUserOffline(Long.valueOf(userId));
        }
    }

    private String getSessionId(Long userId1, Long userId2) {
        List<Long> ids = Arrays.asList(userId1, userId2);
        Collections.sort(ids);
        return "session:" + ids.get(0) + ":" + ids.get(1);
    }

    private void saveMessageToRedis(String sessionId, MessageInfo message) {
        try {
            redisTemplate.opsForList().rightPush(sessionId, gson.toJson(message));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Scheduled(fixedRate = 600000) // 每 10 分钟执行一次
    public void checkInactiveConnections() {
        LocalDateTime now = LocalDateTime.now();
        for (Map.Entry<String, LocalDateTime> entry : lastActiveTimeMap.entrySet()) {
            String userId = entry.getKey();
            LocalDateTime lastActiveTime = entry.getValue();
            if (lastActiveTime.plusMinutes(10).isBefore(now)) {
                WebSocketSession session = sessions.get(userId);
                if (session != null && session.isOpen()) {
                    try {
                        session.close(CloseStatus.GOING_AWAY.withReason("Inactive for 10 minutes"));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    sessions.remove(userId);
                    lastActiveTimeMap.remove(userId);
                    userOnlineService.setUserOffline(Long.valueOf(userId));
                }
            }
        }
    }
}